/**************************************************************************** 
 * Meta，一个轻量级的静态分区Hypervisor 
 *
 * 版权(c) , 2022-
 *
 * 作者:
 *      Tupelo Shen <shenwanjiang2013@163.com>
 *
 * Meta是一个自由软件，你可以在遵循GNU GPLv2协议的条件下，重新修改并发布它。
 *
 ***************************************************************************/

#include <arch/sysregs.h>

#include <cpu.h>
#include <platform.h>

/****************************************************************************
 ************************** 公共变量定义 ************************************
 ****************************************************************************/
// uint64_t CPU_MASTER __attribute__((section(".data")));
// 将其移动到core目录下的公共代码中

/**
 * 功能描述: 依赖于硬件平台的CPU核初始化
 * 入口参数: void
 * 出口参数: void
 * 返 回 值: void
 * 修改日期: 2022-09-21
 */
void cpu_arch_init(cpuid_t cpuid, paddr_t load_addr)
{   
    cpu.arch.mpidr = MRS(MPIDR_EL1);
    if (cpuid == CPU_MASTER) {
        /* power on necessary, but still sleeping, secondary cpu cores
         * Assumes CPU zero is doing this */
        for (size_t cpu_core_id = 0; cpu_core_id < platform.cpu_num;
             cpu_core_id++) {
            if(cpu_core_id == cpuid) continue;
            unsigned long mpdir = cpu_id_to_mpidr(cpu_core_id);
            // TODO: pass config addr in contextid (x0 register)
            int32_t result = psci_cpu_on(mpdir, load_addr, 0);
            if (!(result == PSCI_E_SUCCESS || result == PSCI_E_ALREADY_ON)) {
                ERROR("cant wake up cpu %d", cpu_core_id);
            }
        }
    }
}

/**
 * 功能描述: 将cpu id转换成mpidr
 * 入口参数: cpuid_t id:   cpu id
 * 出口参数: void
 * 返 回 值: unsigned long:    返回mpidr
 * 修改日期: 2022-09-21
 */
unsigned long cpu_id_to_mpidr(cpuid_t id)
{
    return platform_arch_cpuid_to_mpdir(&platform, id);
}

/**
 * 功能描述: 将cpu核从Idle状态转变为`Powerdown`状态
 * 入口参数: void
 * 出口参数: void
 * 返 回 值: void
 * 修改日期: 2022-09-21
 */
void cpu_arch_idle(void)
{
    int64_t err = psci_power_down(PSCI_WAKEUP_IDLE);
    if(err) {
        switch (err) {
            case PSCI_E_NOT_SUPPORTED:
                /* 如果PSCI不支持，则直接发送wfi指令，使核进入低功耗状态 */
                asm volatile("wfi");
                break;
            default:
                ERROR("PSCI cpu%d power down failed with error %ld", cpu.id, err);
        }
    }

    /** 
     * 如果执行到此处，说明使用wfi指令进入休眠状态，
     * 或powerdown成功但唤醒时没有进入指定的`entry point`执行，
     * 为此，人为执行空闲状态唤醒执行流程
     */
    asm volatile("mov sp, %0\n\r" ::"r"(&cpu.stack[STACK_SIZE]));
    cpu_idle_wakeup();
}